









<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>scale4j - 
    tutorials</title>
    <link rel="stylesheet" href="./css/maven-theme.css" type="text/css" />
    <link rel="stylesheet" href="./css/maven-base.css" type="text/css"/>
    <link rel="stylesheet" href="./css/prettify.css" type="text/css"/>
    <link rel="stylesheet" href="./css/print.css" type="text/css" media="print" />
    <script type="text/javascript" src="./js/prettify.js"></script>
    <script type="text/javascript" src="./js/niftycube.js"></script>
    <script type="text/javascript">
        window.onload=function() {
            Nifty("div\#wrapper", "big");
            Nifty("div#breadcrumbs", "small");
            Nifty("div\#navcolumn", "small");
            Nifty("div.source", "medium");
            prettyPrint();
        }
    </script>
        <meta name="Date-Revision-yyyymmdd" content="20110326" />
      </head>
  <body>
    <div id="wrapper">
        <div id="banner">
                        <div id="bannerLeft">
    
                                            <img src="./images/scale4j.png" alt="$alt" />
    
            </div>
                            <div class="clear">
            <hr/>
          </div>
        </div>
        <div id="breadcrumbs">
              
  

  
    
            
  
    
                  <div class="xright">      
  

  
    
            
  
    
             <span id="publishDate">Last Published: 2011-03-26</span>
              &nbsp;| <span id="projectVersion">Version: 0.2</span>
            </div>
          <div class="clear">
            <hr/>
          </div>
        </div>
        <div id="leftColumn">
          <div id="navcolumn">
               
  

  
    
            
  
    
                       <h5>Overview</h5>
            <ul>
              
    <li class="none">
                    <a href="index.html">Description</a>
          </li>
              
    <li class="none">
              <strong>Tutorials</strong>
        </li>
              
    <li class="none">
                    <a href="configuration.html">Configuration</a>
          </li>
              
    <li class="none">
                    <a href="documentation.html">Documentation</a>
          </li>
          </ul>
              <h5>Project Documentation</h5>
            <ul>
              
                
                    
                  
                  
                  
                  
              
        <li class="collapsed">
                    <a href="project-info.html">Project Information</a>
                </li>
              
                
                    
                  
                  
                  
                  
                  
                  
                  
                  
              
        <li class="collapsed">
                    <a href="project-reports.html">Project Reports</a>
                </li>
          </ul>
              <h5>External links</h5>
            <ul>
              
    <li class="none">
                    <a href="http://hazelcast.com" class="externalLink">hazelcast</a>
          </li>
              
    <li class="none">
                    <a href="http://www.springsource.org" class="externalLink">spring</a>
          </li>
              
    <li class="none">
                    <a href="http://www.hibernate.org" class="externalLink">hibernate</a>
          </li>
              
    <li class="none">
                    <a href="http://static.springsource.org/spring-batch" class="externalLink">spring batch</a>
          </li>
              
    <li class="none">
                    <a href="http://static.springsource.org/spring-security/site/index.html" class="externalLink">spring security</a>
          </li>
              
    <li class="none">
                    <a href="http://www.jboss.org/envers" class="externalLink">hibernate envers</a>
          </li>
              
    <li class="none">
                    <a href="http://www.hibernate.org/subprojects/search.html" class="externalLink">hibernate search</a>
          </li>
              
    <li class="none">
                    <a href="http://www.quartz-scheduler.org/" class="externalLink">quartz</a>
          </li>
              
    <li class="none">
                    <a href="http://www.atomikos.com/Publications/WebHome" class="externalLink">atomikos</a>
          </li>
              
    <li class="none">
                    <a href="http://perf4j.codehaus.org" class="externalLink">perf4j</a>
          </li>
              
    <li class="none">
                    <a href="http://vaadin.com/home" class="externalLink">vaadin</a>
          </li>
          </ul>
                                               <a href="http://maven.apache.org/" title="Built by Maven" class="poweredBy">
            <img class="poweredBy"
                 alt="Built by Maven"
                 src="./images/logos/maven-feather.png"
                 width="90"
                 height="30"
                 border="1" />
          </a>
                           
  

  
    
            
  
    
            </div>
        </div>
        <div id="bodyColumn">
          <div id="contentBox">
            
  
      <div class="section"><h2>navigation<a name="navigation"></a></h2>
          <ul>
              <li><a href="#a15_minutes_tutorial">15 minutes tutorial</a></li>
              <li><a href="#a30_minutes_tutorial">30 minutes tutorial</a></li>
          </ul>
      </div>
    <a name="a15_minutes_tutorial"></a><div class="section"><h2 id="15_minutes_tutorial">15 minutes tutorial</h2>
      <p>
        We will create simple exchange rates web application.<br />
        Lets start from mapping our domain entities.<br />
        There are few common classes available in scale4j-persistent module and we will extend
        <a href="./xref/com/katesoft/scale4j/persistent/model/unified/AbstractPersistentEntity.html">AbstractPersistentEntity</a>
        (this is the most common persistent class designed to be used by clients).<br />
        We will also add spring batch and quartz dependencies(both are integrated, but actually optional dependencies) apart from scale4j-rttp and scale4j-web itself.
      </p>
      <div class="source"><pre>
        
    &lt;dependency&gt;
      &lt;groupId&gt;com.mykate-soft&lt;/groupId&gt;
      &lt;artifactId&gt;scale4j-agent&lt;/artifactId&gt;
      &lt;version&gt;0.2&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;com.mykate-soft&lt;/groupId&gt;
      &lt;artifactId&gt;scale4j-web&lt;/artifactId&gt;
      &lt;version&gt;0.2&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.batch&lt;/groupId&gt;
        &lt;artifactId&gt;spring-batch-core&lt;/artifactId&gt;
        &lt;version&gt;2.1.7.RELEASE&lt;/version&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.quartz-scheduler&lt;/groupId&gt;
        &lt;artifactId&gt;quartz&lt;/artifactId&gt;
        &lt;version&gt;1.8.4&lt;/version&gt;
        &lt;exclusions&gt;
          &lt;exclusion&gt;
            &lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;
            &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
          &lt;/exclusion&gt;
          &lt;exclusion&gt;
            &lt;artifactId&gt;slf4j-log4j12&lt;/artifactId&gt;
            &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
          &lt;/exclusion&gt;
        &lt;/exclusions&gt;
    &lt;/dependency&gt;
      </pre></div>

      <p>
        Now lets create our first very simple domain entity - Currency and make this class audit-able.
        Note that you would not need to create primary key as PK already exists in
        <a href="./apidocs/com/katesoft/scale4j/persistent/model/unified/BO.html">BO</a> and populated using distributed unique id generator( com.hazelcast.core.IdGenerator ).
      </p>

      <div class="source"><pre>
package com.katesoft.scale4j.demo.domain;

import com.katesoft.scale4j.persistent.model.unified.AbstractPersistentEntity;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.envers.Audited;

import javax.persistence.Cacheable;
import javax.persistence.Entity;
import javax.persistence.Table;

@Entity
@Table(name = &quot;t_currency&quot;)
@Audited
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Currency extends AbstractPersistentEntity
{
    private String code;

    public Currency(String code)
    {
        this.code = code;
    }

    public Currency()
    {
    }

    public String getCode()
    {
        return code;
    }

    public void setCode(String code)
    {
        this.code = code;
    }
}</pre></div>

      <p>
        Next step is to create spring application context configuration file - assuming we are using default maven project layout lets put currencies-app-context.xml
        under src/main/resources/META-INF/spring folder. Now lets import consolidated spring configuration file from scale4j-rttp sub-project and customize
        hibernate session factory using <b>com.katesoft.scale4j.persistent.spring.postprocessor.HibernateSessionExtensionPostProcessor</b> class:
      </p>

      <div class="source"><pre>
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd&quot;&gt;
  &lt;import resource=&quot;classpath*:META-INF/spring/simple-rttpContext.xml&quot;/&gt;

  &lt;bean
      class=&quot;com.katesoft.scale4j.persistent.spring.postprocessor.HibernateSessionExtensionPostProcessor&quot;&gt;
    &lt;property name=&quot;annotatedClasses&quot;&gt;
      &lt;list&gt;
        &lt;value&gt;com.katesoft.scale4j.demo.domain.Currency&lt;/value&gt;
      &lt;/list&gt;
    &lt;/property&gt;
    &lt;property name=&quot;beanProperties&quot;&gt;
      &lt;props&gt;
        &lt;prop key=&quot;schemaUpdate&quot;&gt;true&lt;/prop&gt;
      &lt;/props&gt;
    &lt;/property&gt;
    &lt;property name=&quot;hibernateProperties&quot;&gt;
      &lt;props&gt;
        &lt;prop key=&quot;hibernate.show_sql&quot;&gt;true&lt;/prop&gt;
        &lt;prop key=&quot;hibernate.dialect&quot;&gt;org.hibernate.dialect.MySQL5InnoDBDialect&lt;/prop&gt;
      &lt;/props&gt;
    &lt;/property&gt;
  &lt;/bean&gt;

  &lt;bean class=&quot;com.katesoft.scale4j.persistent.spring.postprocessor.DatasourcePostProcessor&quot;&gt;
    &lt;property name=&quot;user&quot; value=&quot;xrates&quot;/&gt;
    &lt;property name=&quot;password&quot; value=&quot;xrates&quot;/&gt;
    &lt;property name=&quot;url&quot; value=&quot;jdbc:mysql://localhost/xrates&quot;/&gt;
    &lt;property name=&quot;driverClass&quot; value=&quot;com.mysql.jdbc.Driver&quot;/&gt;
  &lt;/bean&gt;
&lt;/beans&gt;
</pre></div>

      <p>
        You can override any bean properties you like so we will override <b>schemaUpdate</b>(org.springframework.orm.hibernate3.LocalSessionFactoryBean#schemaUpdate).
        You probably would like to notice that this is not acceptable behaviour for productional and you are right, but for this tutorial this is just what we are looking for.
        Hibernate will create necessary tables/indexes/constraints for us.
        Another required customization is to add <b>annotatedClasses</b>(org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean#annotatedClasses) -
        we need to add Currency domain class. I assuming you have installed mysql database engine, so will use this database as persistent store for this tutorial.
        Also we modified hibernate.dialect property and specified org.hibernate.dialect.MySQL5InnoDBDialect as dialect.
      </p>

      <p>
        Before moving on lets customize datasource configuration details. For this purpose we will use
        <a href="./apidocs/com/katesoft/scale4j/persistent/spring/postprocessor/DatasourcePostProcessor.html">DatasourcePostProcessor</a>.
        So I have created xrate user and db for this user and granted full permissions.
      </p>

      <p>
       Now it is time to create few tasks around currency exchange data.
       We will use <a class="externalLink" href="http://currencies.apps.grandtrunk.net/">currency details site</a> and retrieve data using
       <a class="externalLink" href="http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/client/RestTemplate.html">org.springframework.web.client.RestTemplate</a>.
       AvailableCurrenciesTasklet will persist available currencies.
      </p>

      <div class="source"><pre>
package com.katesoft.scale4j.demo.services;

import com.katesoft.scale4j.demo.domain.Currency;
import com.katesoft.scale4j.log.Logger;
import com.katesoft.scale4j.log.LogFactory;
import com.katesoft.scale4j.persistent.client.LocalHibernateTemplate;
import com.katesoft.scale4j.persistent.model.unified.AbstractPersistentEntity;
import org.apache.commons.io.IOUtils;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.web.client.RestTemplate;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.Collection;
import java.util.LinkedHashSet;

import static com.katesoft.scale4j.persistent.utils.DataAccessUtility.fetchFirstRowOrNull;

public class AvailableCurrenciesTasklet implements Tasklet
{
    private ILog logger = LogFactory.getLogger(getClass());
    private RestTemplate template = new RestTemplate();
    private LocalHibernateTemplate hibernateTemplate;

    @SuppressWarnings({&quot;unchecked&quot;})
    @Override
    public RepeatStatus execute(StepContribution contribution,
                                ChunkContext chunkContext)
    {
        String s = template.getForObject(&quot;http://currencies.apps.grandtrunk.net/currencies&quot;, String.class);
        logger.info(s);
        BufferedReader reader = new BufferedReader(new StringReader(s));
        Collection&lt;Currency&gt; currencies = new LinkedHashSet&lt;Currency&gt;();
        try {
            String code;
            while ((code = reader.readLine()) != null) {
                currencies.add(new Currency(code));
            }
            logger.debug(&quot;there are %s available currencies, will persist them&quot;, currencies.size());
            for (Currency currency : currencies) {
                Currency example = new Currency();
                example.cleanBeanProperties();
                example.setCode(currency.getCode());
                AbstractPersistentEntity obj = fetchFirstRowOrNull(hibernateTemplate.findByExample(example));
                if (obj == null) {hibernateTemplate.save(currency);}
            }
        }
        catch (IOException e) {
            logger.error(e);
        }
        finally {
            IOUtils.closeQuietly(reader);
        }
        hibernateTemplate.flush();
        return RepeatStatus.FINISHED;
    }

    @Autowired
    @Required
    public void setHibernateTemplate(LocalHibernateTemplate hibernateTemplate)
    {
        this.hibernateTemplate = hibernateTemplate;
    }
}</pre></div>

      <p>
        As we are good developers we need to create Junit test for our tasklet - to simplify complexity of developing our Junit tests we will add
        scale4j-persistent tests dependency and re-use <a href="./testapidocs/com/katesoft/scale4j/persistent/context/AbstractPersistentTest">AbstractPersistentTest</a>.
      </p>

      <div class="source"><pre>
    &lt;dependency&gt;
      &lt;groupId&gt;com.mykate-soft&lt;/groupId&gt;
      &lt;artifactId&gt;scale4j-persistent&lt;/artifactId&gt;
      &lt;classifier&gt;tests&lt;/classifier&gt;
      &lt;version&gt;0.2&lt;/version&gt;
      &lt;scope&gt;test&lt;/scope&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
      &lt;groupId&gt;org.springframework&lt;/groupId&gt;
      &lt;artifactId&gt;spring-test&lt;/artifactId&gt;
      &lt;scope&gt;test&lt;/scope&gt;
    &lt;/dependency&gt;</pre></div>

      <p>
        Here is our first Junit test class
      </p>

      <div class="source"><pre>
package com.katesoft.scale4j.demo.services;

import com.katesoft.scale4j.demo.domain.Currency;
import com.katesoft.scale4j.common.services.IBeanNameReferences;
import com.katesoft.scale4j.persistent.context.AbstractPersistentTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({&quot;/META-INF/spring/currencies-app-context.xml&quot;})
@TransactionConfiguration(defaultRollback = true)
@Transactional
public class AvailableCurrenciesTaskletTest extends AbstractPersistentTest
{
    private AvailableCurrenciesTasklet availableCurrenciesTasklet;

    @Before
    public void before()
    {
        availableCurrenciesTasklet = new AvailableCurrenciesTasklet();
        availableCurrenciesTasklet.setHibernateTemplate(hibernateTemplate);
    }

    @Test
    public void currenciesPersisted() throws Exception
    {
        availableCurrenciesTasklet.execute(null, null);
        int size = hibernateTemplate.loadAll(Currency.class).size();
        assertThat(size, greaterThan(10));
        hibernateTemplate.clear();
        availableCurrenciesTasklet.execute(null, null);
        assertThat(hibernateTemplate.loadAll(Currency.class).size(), is(size));
    }
}</pre></div>

      <p>
        Lets move on and create more complex ExchangeRate entity.<br />
        Do not forget to add ExchangeRate to annotatedClasses list.
        Notice that we overrided forceAttributesLoad method - scale4j support concept of lazy loading and this concept is used across persistent module.
        forceAttributesLoad is responsible for initializing domain state - it can be more efficient rather than using hibernate lazy loading capability directly.
      </p>

      <div class="source"><pre>
package com.katesoft.scale4j.demo.domain;

import com.katesoft.scale4j.persistent.model.unified.AbstractPersistentEntity;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.envers.Audited;

import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import java.math.BigDecimal;
import java.util.Date;

@Entity
@Table(name = &quot;t_exchange_rates&quot;)
@Audited
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@NamedQueries(value = {@NamedQuery(name = &quot;exchange_rate.find_by_code&quot;,
                                   query = &quot;from ExchangeRate as r where r.currency = ?&quot;)})
public class ExchangeRate extends AbstractPersistentEntity
{
    @JoinColumn(nullable = false, unique = true)
    @OneToOne(fetch = FetchType.LAZY)
    private Currency currency;
    @Column(nullable = false)
    private BigDecimal rate;
    @Column(nullable = false)
    private Date actualDate;

    public ExchangeRate()
    {
    }

    public void setCurrency(Currency currency)
    {
        this.currency = currency;
    }

    public void setRate(BigDecimal rate)
    {
        this.rate = rate;
    }

    public Currency getCurrency()
    {
        return currency;
    }

    public BigDecimal getRate()
    {
        return rate;
    }

    public void setActualDate(Date actualDate)
    {
        this.actualDate = actualDate;
    }

    public Date getActualDate()
    {
        return actualDate;
    }

    @Override
    public void forceAttributesLoad()
    {
        super.forceAttributesLoad();
        initializeProxy(currency);
    }
}</pre></div>

      <p>
        As you probably guess we would need to create additional tasklet that will load currency exchanges(we will use US dollar as base).
        I will skip Junit for this new tasklet - details can be found <a href="./xref-test/com/katesoft/scale4j/demo/services/CurrencyExchangeRatesTaskletTest.html">here</a>.
      </p>

      <div class="source"><pre>
package com.katesoft.scale4j.demo.services;

import com.katesoft.scale4j.demo.domain.Currency;
import com.katesoft.scale4j.demo.domain.ExchangeRate;
import com.katesoft.scale4j.log.Logger;
import com.katesoft.scale4j.log.LogFactory;
import com.katesoft.scale4j.persistent.client.LocalHibernateTemplate;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.web.client.RestTemplate;

import java.io.BufferedReader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import static com.katesoft.scale4j.persistent.utils.DataAccessUtility.fetchFirstRowOrNull;

public class CurrencyExchangeRatesTasklet implements Tasklet
{
    private ILog logger = LogFactory.getLogger(getClass());
    private SimpleDateFormat sf = new SimpleDateFormat(&quot;yyyy-MM-dd&quot;);
    private RestTemplate template = new RestTemplate();
    private LocalHibernateTemplate hibernateTemplate;

    @SuppressWarnings({&quot;unchecked&quot;})
    @Override
    public RepeatStatus execute(StepContribution contribution,
                                final ChunkContext chunkContext)
    {
        ExecutionContext context = chunkContext.getStepContext().
                                            getStepExecution().getJobExecution().getExecutionContext();
        String date = context.containsKey(&quot;date&quot;) ?  context.getString(&quot;date&quot;) : sf.format(new Date());
        List&lt;Currency&gt; currencyList = hibernateTemplate.loadAll(Currency.class);
        for (Currency currency : currencyList) {
            logger.info(&quot;%s *************************************&quot;, currency.getCode());
            try {
                String s = template
                    .getForObject(&quot;http://currencies.apps.grandtrunk.net/getrate/{date}/{fromcode}/{tocode}&quot;,
                    String.class, date, &quot;USD&quot;, currency.getCode());
                logger.info(&quot;got %s for values = %s&quot;, s,
                             Arrays.toString(new String[]{date, &quot;USD&quot;, currency.getCode()}));
                BufferedReader reader = new BufferedReader(new StringReader(s));
                try {
                    String next;
                    while ((next = reader.readLine()) != null) {
                        ExchangeRate exchangeRate = new ExchangeRate();
                        exchangeRate.setCurrency(currency);
                        exchangeRate.setRate(new BigDecimal(next));
                        exchangeRate.setActualDate(sf.parse(date));
                        ExchangeRate existingRate =
                            (ExchangeRate) fetchFirstRowOrNull(hibernateTemplate.
                                        findByNamedQuery(&quot;exchange_rate.find_by_code&quot;, currency));
                        if (existingRate != null) {
                            existingRate.setRate(exchangeRate.getRate());
                            existingRate.setActualDate(sf.parse(date));
                            hibernateTemplate.update(existingRate);
                        }
                        else {
                            hibernateTemplate.save(exchangeRate);
                        }
                        hibernateTemplate.flush();
                    }
                }
                finally {
                    reader.close();
                }
            }
            catch (Exception e) {
                logger.warn(e);
            }
        }
        hibernateTemplate.flush();
        return RepeatStatus.FINISHED;
    }

    @Autowired
    @Required
    public void setHibernateTemplate(LocalHibernateTemplate hibernateTemplate)
    {
        this.hibernateTemplate = hibernateTemplate;
    }}</pre></div>
      <p>
        At this stage we have demo application with 2 persistent entities(both are audit-able) and 2 tasklets.
        Actually this is very simple tutorial which shows how easily we can integrate domain entities into scale4j persistent module.
        That is all for our first 15 minutes tutorial - now you know how to:
      </p>

      <ul>
        <li>Register domain entities(simply by extending com.katesoft.scale4j.persistent.model.unified.AbstractPersistentEntity)</li>
        <li>Import jvmcluster-rttp spring context functionality using META-INF/spring/simple-rttpContext.xml</li>
        <li>Customize hibernate session factory(by using com.katesoft.scale4j.persistent.spring.postprocessor.HibernateSessionExtensionPostProcessor)</li>
        <li>Customize database connection details(by using com.katesoft.scale4j.persistent.spring.postprocessor.DatasourcePostProcessor)</li>
      </ul>

      <p>
        Now it is time to move to <a href="#a30_minutes_tutorial">30 minutes tutorial</a> where you will find how to:
      </p>

      <ul>
        <li>Work with historical data(we will use exchange entity as example)</li>
        <li>Use built-in quartz scheduler-spring batch bridge and schedule distributed jobs</li>
        <li>define 'services' and launch them using scale4j-agent module</li>
        <li>Explore JMX and web monitoring console(hazelcast, perf4j, log4j, quartz) functionality</li>
      </ul>
    </div>

    <a name="a30_minutes_tutorial"></a><div class="section"><h2 id="30_minutes_tutorial">30 minutes tutorial</h2>
      <p>
        So it is time to prepare 2 application services for our scale4j-agent module.<br />
        First service will run x-currency update job and will expose jobs-related functionality through web-console(we will use built-in quartz monitoring web application).
        Second service will be web-server for our x-rates web application - we will write simple application using vaading framework.
        Each application service at runtime will have it's own JVM and <a href="./xref/com/katesoft/scale4j/agent/Agent.html">com.katesoft.scale4j.agent.Agent</a> will manage lifecycle of those services.
      </p>

      <p>
        Lets look through spring configuration file for first service.
        We have created 2 spring batch jobs(end of day and intraday) and defined 2 quartz triggers.
        Intraday trigger will fire job execution each 5 minutes and EndOfDay trigger will fire execution at 23.00.
        Also we created 2 job details beans(eod.job.details,intraday.job.details) - those beans are actually bridge between quartz and spring batch.
        In fact you are not restricted to use <a href="./xref/com/katesoft/scale4j/rttp/jobs/RttpJobDetailBean.html">RttpJobDetailBean</a> class,
        use can use <a class="externalLink" href="http://static.springsource.org/spring/docs/1.2.9/reference/scheduling.html">org.springframework.scheduling.quartz.JobDetailBean</a> directly.
        RttpJobDetailBean is used in case when you need to run spring batch jobs from quartz job scheduler.
        The last one bean definition is SchedulerJobsRegistrator - this class is used to register job details/triggers/event listeners within actual <b>org.quartz.Scheduler</b> instance
        (we used autoDetectJobs feature - all triggers and job details within application context will be registered).
        Worth nothing to include scale4j web monitoring console running on port 8967 - it is just matter of adding <a href="./xref/com/katesoft/scale4j/web/webconsole/WebMonitor.html">WebMonitor</a>.
        Please note that we imported 2 additional spring configuration files(rttp-jobs-context.xml is job scheduler container and rttp-batch-context.xml is container for spring batch jobs).
      </p>

<div class="source"><pre>
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans:beans xmlns=&quot;http://www.springframework.org/schema/batch&quot;
    xmlns:beans=&quot;http://www.springframework.org/schema/beans&quot;
    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xmlns:p=&quot;http://www.springframework.org/schema/p&quot;
    xsi:schemaLocation=&quot;
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/batch
           http://www.springframework.org/schema/batch/spring-batch-2.1.xsd&quot;&gt;
  &lt;beans:import resource=&quot;classpath*:META-INF/spring/currencies-app-context.xml&quot;/&gt;
  &lt;beans:import resource=&quot;classpath*:META-INF/spring/rttp-jobs-context.xml&quot;/&gt;
  &lt;beans:import resource=&quot;classpath*:META-INF/spring/rttp-batch-context.xml&quot;/&gt;

  &lt;job id=&quot;intraday.job&quot; job-repository=&quot;jobRepository&quot;&gt;
    &lt;step id=&quot;intraday.step1&quot; next=&quot;intraday.step2&quot;&gt;
      &lt;tasklet ref=&quot;available.currencies.tasklet&quot;/&gt;
    &lt;/step&gt;
    &lt;step id=&quot;intraday.step2&quot;&gt;
      &lt;tasklet ref=&quot;currency.exchange.rates.tasklet&quot;/&gt;
    &lt;/step&gt;
  &lt;/job&gt;

  &lt;job id=&quot;eod.job&quot; job-repository=&quot;jobRepository&quot;&gt;
    &lt;step id=&quot;eod.step1&quot; next=&quot;eod.step2&quot;&gt;
      &lt;tasklet ref=&quot;available.currencies.tasklet&quot;/&gt;
    &lt;/step&gt;
    &lt;step id=&quot;eod.step2&quot;&gt;
      &lt;tasklet ref=&quot;currency.exchange.rates.tasklet&quot;/&gt;
    &lt;/step&gt;
  &lt;/job&gt;

  &lt;beans:bean
      class=&quot;com.katesoft.scale4j.rttp.jobs.SchedulerJobsRegistrator&quot;
      p:autoDetectJobs=&quot;true&quot;/&gt;

  &lt;beans:bean
      id=&quot;intraday.trigger&quot;
      class=&quot;org.springframework.scheduling.quartz.SimpleTriggerBean&quot;
      p:repeatInterval=&quot;300000&quot;
      p:jobDetail-ref=&quot;intraday.job.details&quot;/&gt;

  &lt;beans:bean
      id=&quot;eod.trigger&quot;
      class=&quot;org.springframework.scheduling.quartz.CronTriggerBean&quot;
      p:cronExpression=&quot;0 0 23 * * ?&quot;
      p:jobDetail-ref=&quot;eod.job.details&quot;/&gt;

  &lt;beans:bean
      id=&quot;eod.job.details&quot;
      class=&quot;com.katesoft.scale4j.rttp.jobs.RttpJobDetailBean&quot;
      p:group=&quot;scale4j-demo-group&quot;
      p:name=&quot;[end of day] job&quot;
      p:jobClass=&quot;com.katesoft.scale4j.rttp.jobs.SpringBatchJobLauncher&quot;
      p:job-ref=&quot;eod.job&quot;/&gt;

  &lt;beans:bean
      id=&quot;intraday.job.details&quot;
      class=&quot;com.katesoft.scale4j.rttp.jobs.RttpJobDetailBean&quot;
      p:group=&quot;scale4j-demo-group&quot;
      p:name=&quot;[x-rates-update] job&quot;
      p:jobClass=&quot;com.katesoft.scale4j.rttp.jobs.SpringBatchJobLauncher&quot;
      p:job-ref=&quot;intraday.job&quot;/&gt;

  &lt;beans:bean
      id=&quot;web.monitor&quot;
      class=&quot;com.katesoft.scale4j.web.webconsole.WebMonitor&quot;&gt;
    &lt;beans:property name=&quot;connectors&quot;&gt;
      &lt;beans:list&gt;
        &lt;beans:bean class=&quot;org.mortbay.jetty.nio.SelectChannelConnector&quot;&gt;
          &lt;beans:property name=&quot;port&quot; value=&quot;8967&quot;/&gt;
        &lt;/beans:bean&gt;
      &lt;/beans:list&gt;
    &lt;/beans:property&gt;
  &lt;/beans:bean&gt;
&lt;/beans:beans&gt;</pre></div>

        <p>
          And the second spring configuration context file is pretty simple as we actually need to define just jetty server to host our web application.
        </p>

        <div class="source"><pre>
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans xmlns=&quot;http://www.springframework.org/schema/beans&quot;
    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd&quot;&gt;
  &lt;bean
      id=&quot;x-rates.jetty.server&quot;
      class=&quot;org.mortbay.jetty.Server&quot;
      init-method=&quot;start&quot;
      destroy-method=&quot;stop&quot;&gt;
    &lt;property name=&quot;connectors&quot;&gt;
      &lt;list&gt;
        &lt;bean class=&quot;org.mortbay.jetty.nio.SelectChannelConnector&quot;&gt;
          &lt;property name=&quot;port&quot; value=&quot;8968&quot;/&gt;
        &lt;/bean&gt;
      &lt;/list&gt;
    &lt;/property&gt;
    &lt;property name=&quot;handler&quot;&gt;
      &lt;bean class=&quot;org.mortbay.jetty.webapp.WebAppContext&quot;&gt;
        &lt;property name=&quot;contextPath&quot; value=&quot;/x-rates&quot;/&gt;
        &lt;property name=&quot;baseResource&quot;&gt;
          &lt;bean class=&quot;org.mortbay.resource.ResourceCollection&quot;&gt;
            &lt;constructor-arg value=&quot;./target/scale4j-aggregator-0.2&quot;/&gt;
          &lt;/bean&gt;
        &lt;/property&gt;
      &lt;/bean&gt;
    &lt;/property&gt;
  &lt;/bean&gt;
&lt;/beans&gt;</pre></div>

        <p>
          The only one remaining step for second service is to create actual web application class. We added date box which allows you to retrieve historical data
          (retrieve rate for specific date).
        </p>

        <div class="source"><pre>
package com.katesoft.scale4j.demo.web;

import com.katesoft.scale4j.common.services.IBeanNameReferences;
import com.katesoft.scale4j.demo.domain.ExchangeRate;
import com.katesoft.scale4j.log.LogFactory;
import com.katesoft.scale4j.log.Logger;
import com.katesoft.scale4j.persistent.client.LocalHibernateDaoSupport;
import com.katesoft.scale4j.persistent.types.RevisionData;
import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.ui.PopupDateField;
import com.vaadin.ui.Table;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Collection;
import java.util.Date;
import java.util.LinkedHashSet;

/** @author kate2007 */
public class XRateApplication extends com.vaadin.Application
{
    private static Logger LOGGER = LogFactory.getLogger(XRateApplication.class);
    private static ClassPathXmlApplicationContext context;

    @Override
    public void init()
    {
        final Window main = new Window(&quot;x-rates browser&quot;);
        setMainWindow(main);
        setTheme(&quot;runo&quot;);
        main.addListener(new Window.CloseListener()
        {
            @Override
            public void windowClose(Window.CloseEvent e)
            {
                getMainWindow().getApplication().close();
            }
        });
        if (context == null) {
            context = new ClassPathXmlApplicationContext(&quot;META-INF/spring/currencies-app-context.xml&quot;);
            context.registerShutdownHook();
        }
        final LocalHibernateDaoSupport hibernateDaoSupport =
                 (LocalHibernateDaoSupport) context.getBean(IBeanNameReferences.HIBERNATE_DAO_SUPPORT);
        final Table table = new Table();
        table.setCaption(&quot;x-rates&quot;);
        table.addContainerProperty(&quot;USD&quot;, String.class, null);
        table.addContainerProperty(&quot;CURRENCY&quot;, String.class, null);
        table.addContainerProperty(&quot;RATE&quot;, String.class, null);
        final Collection&lt;ExchangeRate&gt; currentRates =
                     hibernateDaoSupport.getLocalHibernateTemplate().loadAll(ExchangeRate.class, true);
        renderTable(table, currentRates);
        final PopupDateField datetime = new PopupDateField(&quot;Please select the starting time:&quot;);
        datetime.setValue(new java.util.Date());
        datetime.setResolution(PopupDateField.RESOLUTION_DAY);
        datetime.addListener(new ValueChangeListener()
        {
            @Override
            public void valueChange(ValueChangeEvent event)
            {
                if (event.getProperty().getValue() != null) {
                    table.removeAllItems();
                    renderTable(table, loadRateForDate(hibernateDaoSupport,
                                (Date) event.getProperty().getValue(), currentRates));
                }
            }
        });
        datetime.setImmediate(true);
        VerticalLayout verticalLayout = new VerticalLayout();
        verticalLayout.addComponent(datetime);
        verticalLayout.addComponent(table);
        main.addComponent(verticalLayout);
    }

    protected void renderTable(Table table,
                               Collection&lt;ExchangeRate&gt; exchangeRates)
    {
        for (ExchangeRate rate : exchangeRates) {
            LOGGER.info(&quot;USD-%s:=&gt;%s&quot;, rate.getCurrency().getCode(), rate.getRate().toString());
            table.addItem(new Object[]{&quot;USD&quot;, rate.getCurrency().getCode(), rate.getRate().toString()}, rate.getCurrency().getCode());
        }
        table.setPageLength(exchangeRates.size());
    }

    protected Collection&lt;ExchangeRate&gt; loadRateForDate(LocalHibernateDaoSupport hibernateDaoSupport,
                                                       Date date,
                                                       Collection&lt;ExchangeRate&gt; currentRates)
    {
        Collection&lt;Long&gt; uniqueIdentifiers = new LinkedHashSet&lt;Long&gt;();
        Collection&lt;ExchangeRate&gt; rates = new LinkedHashSet&lt;ExchangeRate&gt;();
        for (ExchangeRate currentRate : currentRates) {
            uniqueIdentifiers.add(currentRate.getUniqueIdentifier());
        }
        Collection&lt;RevisionData&lt;ExchangeRate&gt;&gt; values =
            hibernateDaoSupport.latestRevisionForDate(ExchangeRate.class, date, uniqueIdentifiers, &quot;actualDate&quot;).values();
        for (RevisionData&lt;ExchangeRate&gt; value : values) {
            rates.add(value.getEntityAtRevision());
        }
        return rates;
    }
}</pre></div>

     <p>
         OK, we almost done - so we will create applicationConfiguration file for scale4j-agent module which will take responsibility for managing lifecycle of
         our 2 application services and will create separate JVM process for each service(we will be able to manage services through jmx console and can for example
         stop/restart one of them without affecting another). This configuration file itself is very simple and defines standard JVM options/configurations and contains list of service definition.<br />
         You can specify any mainClass(with public static void method), but for this tutorial we will use <a href="./xref/com/katesoft/scale4j/agent/SpringConfigurationRunner.html">SpringConfigurationRunner</a>
         which allows you to launch spring-based applications(you would need to pass spring config locations as application argument). So we will pass META-INF/spring/jobs-service-container.xml as argument to first service,
         and META-INF/spring/webapp-service-container.xml to second. Also for demo purpose we specified additional jvm options for both services and specified log4j.xml location.
         Notice how we specified jmx port for agent itself and for our services, now you will be able to connect to both agent/services using remote jmx connection.
         Worth nothing to specify debug port - we can then use remote debug connection from eclipse, idea.
     </p>

     <div class="source"><pre>
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;applicationConfiguration
    xmlns=&quot;http://katesoft.com/xml/jvmcluster&quot;
    xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://katesoft.com/xml/jvmcluster&quot;
    jmxPort=&quot;10774&quot;&gt;

  &lt;service name=&quot;service1&quot; mainClass=&quot;com.katesoft.scale4j.agent.SpringConfigurationRunner&quot; jmxPort=&quot;10775&quot;&gt;
    &lt;jvmConfiguration vmType=&quot;server&quot;&gt;
      &lt;memoryConfiguration initialHeap=&quot;32&quot; maxHeap=&quot;128&quot;/&gt;
      &lt;debugConfiguration port=&quot;11775&quot; debugSuspend=&quot;false&quot;/&gt;
      &lt;jvmArg value=&quot;-XX:+UseBiasedLocking&quot;/&gt;
    &lt;/jvmConfiguration&gt;
    &lt;logConfiguration location=&quot;log4j/log4j.xml&quot;/&gt;
    &lt;applicationArg value=&quot;META-INF/spring/jobs-service-container.xml&quot;/&gt;
  &lt;/service&gt;

  &lt;service name=&quot;service2&quot; mainClass=&quot;com.katesoft.scale4j.agent.SpringConfigurationRunner&quot; jmxPort=&quot;10776&quot;&gt;
    &lt;jvmConfiguration vmType=&quot;server&quot;&gt;
      &lt;memoryConfiguration initialHeap=&quot;32&quot; maxHeap=&quot;128&quot;/&gt;
      &lt;debugConfiguration port=&quot;11776&quot; debugSuspend=&quot;false&quot;/&gt;
      &lt;jvmArg value=&quot;-XX:-UseSpinning&quot;/&gt;
    &lt;/jvmConfiguration&gt;
    &lt;logConfiguration location=&quot;log4j/log4j.xml&quot;/&gt;
    &lt;applicationArg value=&quot;META-INF/spring/webapp-service-container.xml&quot;/&gt;
  &lt;/service&gt;

  &lt;globalConfiguration&gt;
    &lt;jvmArg value=&quot;-XX:-UseParallelGC&quot;/&gt;
    &lt;jvmArg value=&quot;-XX:+OptimizeStringConcat&quot;/&gt;

    &lt;systemProperty name=&quot;file.encoding&quot; value=&quot;UTF-8&quot;/&gt;
    &lt;systemProperty name=&quot;com.sun.management.jmxremote.authenticate&quot; value=&quot;false&quot;/&gt;
    &lt;systemProperty name=&quot;com.sun.management.jmxremote.ssl&quot; value=&quot;false&quot;/&gt;
  &lt;/globalConfiguration&gt;
&lt;/applicationConfiguration&gt;</pre></div>

        <p>
            And now it is time to start application - for demo purpose lets use exec maven plugin.
            You can use run.bat from bin directory and you probably would need to run load_data.bat before(which will download rates and store data).
        </p>

<div class="source"><pre>
          &lt;plugin&gt;
            &lt;groupId&gt;org.codehaus.mojo&lt;/groupId&gt;
            &lt;artifactId&gt;exec-maven-plugin&lt;/artifactId&gt;
            &lt;configuration&gt;
              &lt;executable&gt;java&lt;/executable&gt;
              &lt;arguments&gt;
                &lt;argument&gt;-Dcom.sun.management.jmxremote&lt;/argument&gt;
                &lt;argument&gt;-Dcom.sun.management.jmxremote.port=10774&lt;/argument&gt;
                &lt;argument&gt;-Dcom.sun.management.jmxremote.authenticate=false&lt;/argument&gt;
                &lt;argument&gt;-Dcom.sun.management.jmxremote.ssl=false&lt;/argument&gt;
                &lt;argument&gt;-Xms8m&lt;/argument&gt;
                &lt;argument&gt;-Xmx16m&lt;/argument&gt;
                &lt;argument&gt;-classpath&lt;/argument&gt;
                &lt;classpath/&gt;
                &lt;argument&gt;com.katesoft.scale4j.agent.Agent&lt;/argument&gt;
              &lt;/arguments&gt;
            &lt;/configuration&gt;
          &lt;/plugin&gt;</pre></div>

        <p>
            Done. Lets discover results:
        </p>

        <ul>
            <li>our web application is available under <a class="externalLink" href="http://localhost:8968/x-rates">http://localhost:8968/x-rates</a>(you can select actual date to retrieve historical data)</li>
            <li>agent can be managed using remote JMX connection localhost:10774(scale4j.agent node)</li>
            <li>first  service can be managed using remote JMX connection localhost:10775</li>
            <li>second service can be managed using remote JMX connection localhost:10776</li>
            <li>you can debug first application by using remote connect to localhost:11775</li>
            <li>you can debug second application by using remote connect to localhost:11776</li>
            <li>log4j can be configured at runtime for first service through monitoring console under <a class="externalLink" href="http://localhost:8967/log4j">http://localhost:8967/log4j</a></li>
            <li>real-time performance statistic can be be found under <a class="externalLink" href="http://localhost:8967/perf4j">http://localhost:8967/perf4j</a>(probably perf4j graphs are empty at the moment,
                but you will be able to see statistic after doing some stuff with LocalHibernateDaoSupport or LocalHibernateTemplate)</li>
            <li>hazelcast cluster itself can be managed using <a class="externalLink" href="http://localhost:8967/hazelcast">http://localhost:8967/hazelcast</a></li>
        </ul>

        <p>
            And a very simple architecture diagram <img src="./images/scale4j_arch.jpg" height="600" width="800" alt="" />
        </p>

        <p>
            Hope this tutorial was useful! Thanks for your time. If you are feeling comfortable with scale4j then please visit
            <a href="./configuration.html">configuration guide</a> and <a href="./documentation.html">documentation</a> pages.
        </p>
    </div>
  

          </div>
        </div>
        <div class="clear">
          <hr/>
        </div>
        <div id="footer">
          <div class="xright">Copyright &#169;          
   
          2010-2011
    
                    <a href="http://mykate-soft.com">mykate-soft</a>.
            All Rights Reserved.    
  

  
    
            
  
    
  </div>
          <div class="clear">
            <hr/>
          </div>
        </div>
    </div>
  </body>
</html>
